﻿using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Diagnostics;
using System.Security.Permissions;

namespace Guog.Common.Controls.FlashTrackBar
{
    /// <summary>
    /// 说明:源代码来自 MSDN
    /// 一个自定义的进度条控件
    /// </summary>
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public class FlashTrackBar : Control
    {
        #region 成员变量
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private Container m_components;

        private const int m_leftRightBorder = 10;
        private int m_value = 0;
        private int m_min = 0;
        private int m_max = 100;
        private bool m_showPercentage = false;
        private bool m_showValue = false;
        private bool m_allowUserEdit = true;
        private bool m_showGradient = true;
        private int m_dragValue = 0;
        private bool m_dragging = false;
        private Color m_startColor = Color.Green;
        private Color m_endColor = Color.White;
        private EventHandler m_onValueChanged;
        private Brush m_baseBackground = null;
        private Brush m_backgroundDim = null;
        private byte m_darkenBy = 200;
        #endregion 成员变量

        #region 构造
        public FlashTrackBar()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();
            
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            Debug.Assert(GetStyle(ControlStyles.ResizeRedraw), "Should be redraw!");
        }
        #endregion 构造

        #region 方法

        #region private
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            m_components = new Container();
            //this.ForeColor = System.Drawing.Color.White;
            //this.BackColor = System.Drawing.Color.Black;
            //this.Size = new System.Drawing.Size(100, 23);
            this.Text = "FlashTrackBar";
        }

        private void SetDragValue(Point mouseLocation)
        {

            Rectangle client = ClientRectangle;

            if (client.Contains(mouseLocation))
            {
                float percentage = (float)mouseLocation.X / (float)ClientRectangle.Width;
                int newDragValue = (int)(percentage * (float)(m_max - m_min));
                if (newDragValue != m_dragValue)
                {
                    int old = m_dragValue;
                    m_dragValue = newDragValue;
                    OptimizedInvalidate(old, m_dragValue);
                }
            }
            else
            {
                if (client.Y <= mouseLocation.Y && mouseLocation.Y <= client.Y + client.Height)
                {
                    if (mouseLocation.X <= client.X && mouseLocation.X > client.X - m_leftRightBorder)
                    {
                        int newDragValue = m_min;
                        if (newDragValue != m_dragValue)
                        {
                            int old = m_dragValue;
                            m_dragValue = newDragValue;
                            OptimizedInvalidate(old, m_dragValue);
                        }
                    }
                    else if (mouseLocation.X >= client.X + client.Width && mouseLocation.X < client.X + client.Width + m_leftRightBorder)
                    {
                        int newDragValue = m_max;
                        if (newDragValue != m_dragValue)
                        {
                            int old = m_dragValue;
                            m_dragValue = newDragValue;
                            OptimizedInvalidate(old, m_dragValue);
                        }
                    }
                }
                else
                {
                    if (m_dragValue != m_value)
                    {
                        int old = m_dragValue;
                        m_dragValue = m_value;
                        OptimizedInvalidate(old, m_dragValue);
                    }
                }
            }
        }

        private void OptimizedInvalidate(int oldValue, int newValue)
        {
            Rectangle client = ClientRectangle;

            float oldPercentValue = ((float)oldValue / ((float)Max - (float)Min));
            int oldNonDimLength = (int)(oldPercentValue * (float)client.Width);

            float newPercentValue = ((float)newValue / ((float)Max - (float)Min));
            int newNonDimLength = (int)(newPercentValue * (float)client.Width);

            int min = Math.Min(oldNonDimLength, newNonDimLength);
            int max = Math.Max(oldNonDimLength, newNonDimLength);

            Rectangle invalid = new Rectangle(
                client.X + min,
                client.Y,
                max - min,
                client.Height);

            Invalidate(invalid);

            string oldToDisplay;
            string newToDisplay;

            if (ShowPercentage)
            {
                oldToDisplay = Convert.ToString((int)(oldPercentValue * 100f)) + "%";
                newToDisplay = Convert.ToString((int)(newPercentValue * 100f)) + "%";
            }
            else if (ShowValue)
            {
                oldToDisplay = Convert.ToString(oldValue);
                newToDisplay = Convert.ToString(newValue);
            }
            else
            {
                oldToDisplay = null;
                newToDisplay = null;
            }

            if (oldToDisplay != null && newToDisplay != null)
            {
                Graphics g = CreateGraphics();
                SizeF oldFontSize = g.MeasureString(oldToDisplay, Font);
                SizeF newFontSize = g.MeasureString(newToDisplay, Font);
                RectangleF oldFontRect = new RectangleF(new PointF(0, 0), oldFontSize);
                RectangleF newFontRect = new RectangleF(new PointF(0, 0), newFontSize);
                oldFontRect.X = (client.Width - oldFontRect.Width) / 2;
                oldFontRect.Y = (client.Height - oldFontRect.Height) / 2;
                newFontRect.X = (client.Width - newFontRect.Width) / 2;
                newFontRect.Y = (client.Height - newFontRect.Height) / 2;

                Invalidate(new Rectangle((int)oldFontRect.X, (int)oldFontRect.Y, (int)oldFontRect.Width, (int)oldFontRect.Height));
                Invalidate(new Rectangle((int)newFontRect.X, (int)newFontRect.Y, (int)newFontRect.Width, (int)newFontRect.Height));
            }
        }
        #endregion private

        #region protected
        /// <summary>
        /// 释放资源
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (m_components != null)
                {
                    m_components.Dispose();
                }
            }
            base.Dispose(disposing);
        }



        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (!m_allowUserEdit)
            {
                return;
            }
            Capture = true;
            m_dragging = true;
            SetDragValue(new Point(e.X, e.Y));
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (!m_allowUserEdit || !m_dragging)
            {
                return;
            }
            SetDragValue(new Point(e.X, e.Y));
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (!m_allowUserEdit || !m_dragging)
            {
                return;
            }
            Capture = false;
            m_dragging = false;
            m_value = m_dragValue;
            OnValueChanged(EventArgs.Empty);
        }

        protected override void OnPaint(PaintEventArgs e)
        {

            base.OnPaint(e);
            if (m_baseBackground == null)
            {
                if (m_showGradient)
                {
                    m_baseBackground = new LinearGradientBrush(new Point(0, 0),
                                                             new Point(ClientSize.Width, 0),
                                                             StartColor,
                                                             EndColor);
                }
                else if (BackgroundImage != null)
                {
                    m_baseBackground = new TextureBrush(BackgroundImage);
                }
                else
                {
                    m_baseBackground = new SolidBrush(BackColor);
                }
            }

            if (m_backgroundDim == null)
            {
                m_backgroundDim = new SolidBrush(Color.FromArgb(DarkenBy, BackColor));
            }

            Rectangle toDim = ClientRectangle;
            float percentValue = ((float)Value / ((float)Max - (float)Min));
            int nonDimLength = (int)(percentValue * (float)toDim.Width);
            toDim.X += nonDimLength;
            toDim.Width -= nonDimLength;


            string text = Text;
            string toDisplay = null;
            RectangleF textRect = new RectangleF();

            if (ShowPercentage || ShowValue || text.Length > 0)
            {

                if (ShowPercentage)
                {
                    toDisplay = Convert.ToString((int)(percentValue * 100f)) + "%";
                }
                else if (ShowValue)
                {
                    toDisplay = Convert.ToString(Value);
                }
                else
                {
                    toDisplay = text;
                }

                SizeF textSize = e.Graphics.MeasureString(toDisplay, Font);
                textRect.Width = textSize.Width;
                textRect.Height = textSize.Height;
                textRect.X = (ClientRectangle.Width - textRect.Width) / 2;
                textRect.Y = (ClientRectangle.Height - textRect.Height) / 2;
            }

            e.Graphics.FillRectangle(m_baseBackground, ClientRectangle);
            e.Graphics.FillRectangle(m_backgroundDim, toDim);
            e.Graphics.Flush();
            if (toDisplay != null && toDisplay.Length > 0)
            {
                e.Graphics.DrawString(toDisplay, Font, new SolidBrush(ForeColor), textRect);
            }
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            Invalidate();
        }

        protected override void OnBackColorChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            if ((m_baseBackground != null) && (!m_showGradient))
            {
                m_baseBackground.Dispose();
                m_baseBackground = null;
            }
        }

        protected override void OnBackgroundImageChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            if ((m_baseBackground != null) && (!m_showGradient))
            {
                m_baseBackground.Dispose();
                m_baseBackground = null;
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (m_baseBackground != null)
            {
                m_baseBackground.Dispose();
                m_baseBackground = null;
            }
        }

        protected virtual void OnValueChanged(EventArgs e)
        {
            if (m_onValueChanged != null)
            {
                m_onValueChanged.Invoke(this, e);
            }
        }
        #endregion protected

        #region public
        public bool ShouldSerializeEndColor()
        {
            return !(m_endColor == Color.LimeGreen);
        }

        public bool ShouldSerializeStartColor()
        {
            return !(m_startColor == Color.Red);
        }
        #endregion public
        #endregion 方法

        #region 属性
        /// <summary>
        /// 获取或设置用户是否可以通过单击和拖动来更改闪烁跟踪条的值
        /// </summary>
        [
        Category("FlashTrackBarSetting"), DefaultValue(true),
        Description("指示用户是否可以通过单击和拖动来更改闪烁跟踪条的值.")
        ]
        public bool AllowUserEdit
        {
            get
            {
                return m_allowUserEdit;
            }
            set
            {
                if (value != m_allowUserEdit)
                {
                    m_allowUserEdit = value;
                    if (!m_allowUserEdit)
                    {
                        Capture = false;
                        m_dragging = false;
                    }
                }
            }
        }

        /// <summary>
        /// 获取或设置跟踪条的结束颜色
        /// </summary>
        [Category("FlashTrackBarSetting"), Description("获取或设置跟踪条的结束颜色.")]
        public Color EndColor
        {
            get
            {
                return m_endColor;
            }
            set
            {
                m_endColor = value;
                if (m_baseBackground != null && m_showGradient)
                {
                    m_baseBackground.Dispose();
                    m_baseBackground = null;
                }
                Invalidate();
            }
        }

        /// <summary>
        /// 获取或设置根据前景渐变加深背景颜色的程度
        /// </summary>
        [Category("FlashTrackBarSetting"),
         Editor(typeof(FlashTrackBarDarkenByEditor), typeof(UITypeEditor)),
         DefaultValue((byte)200),
         Description("指定根据前景渐变加深背景颜色的程度.")
        ]
        public byte DarkenBy
        {
            get
            {
                return m_darkenBy;
            }
            set
            {
                if (value != m_darkenBy)
                {
                    m_darkenBy = value;
                    if (m_backgroundDim != null)
                    {
                        m_backgroundDim.Dispose();
                        m_backgroundDim = null;
                    }
                    OptimizedInvalidate(Value, m_max);
                }
            }
        }


        /// <summary>
        /// 获取或设置跟踪条的最大值
        /// </summary>
        [
            Category("FlashTrackBarSetting"),
            DefaultValue(100),
            Description("获取或设置跟踪条的最大值.")
        ]
        public int Max
        {
            get
            {
                return m_max;
            }
            set
            {
                if (m_max != value)
                {
                    m_max = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置跟踪条的最小值
        /// </summary>
        [Category("FlashTrackBarSetting"), DefaultValue(0),Description("跟踪条的最小值.")]
        public int Min
        {
            get
            {
                return m_min;
            }
            set
            {
                if (m_min != value)
                {
                    m_min = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置渐变的开始颜色
        /// </summary>
        [Category("FlashTrackBarSetting"), Description("渐变的开始颜色.")]
        public Color StartColor
        {
            get
            {
                return m_startColor;
            }
            set
            {
                m_startColor = value;
                if (m_baseBackground != null && m_showGradient)
                {
                    m_baseBackground.Dispose();
                    m_baseBackground = null;
                }
                Invalidate();
            }
        }

        /// <summary>
        /// 获取或设置是否在渐变之上显示百分比
        /// </summary>
        [
            Category("FlashTrackBarSetting"),
            RefreshProperties(RefreshProperties.Repaint),
            DefaultValue(false),
            Description("是否在渐变之上显示百分比.")
        ]
        public bool ShowPercentage
        {
            get
            {
                return m_showPercentage;
            }
            set
            {
                if (value != m_showPercentage)
                {
                    m_showPercentage = value;
                    if (m_showPercentage)
                    {
                        m_showValue = false;
                    }
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置是否在渐变之上显示当前值
        /// </summary>
        [
            Category("FlashTrackBarSetting"),
            RefreshProperties(RefreshProperties.Repaint),
            DefaultValue(false),
            Description("是否在渐变之上显示当前值")
        ]
        public bool ShowValue
        {
            get
            {
                return m_showValue;
            }
            set
            {
                if (value != m_showValue)
                {
                    m_showValue = value;
                    if (m_showValue)
                    {
                        m_showPercentage = false;
                    }
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置跟踪条是否应显示表明当前值的颜色渐变
        /// </summary>
        [
            Category("FlashTrackBarSetting"),
            DefaultValue(true),
            Description("跟踪条是否应显示表明当前值的颜色渐变.")
        ]
        public bool ShowGradient
        {
            get
            {
                return m_showGradient;
            }
            set
            {
                if (value != m_showGradient)
                {
                    m_showGradient = value;
                    if (m_baseBackground != null)
                    {
                        m_baseBackground.Dispose();
                        m_baseBackground = null;
                    }
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// 获取或设置跟踪条的当前值
        /// </summary>
        [
            Category("FlashTrackBarSetting"),
            Editor(typeof(FlashTrackBarValueEditor), typeof(UITypeEditor)),
            DefaultValue(0),
            Description("跟踪条的当前值.")
        ]
        public int Value
        {
            get
            {
                if (m_dragging)
                {
                    return m_dragValue;
                }
                return m_value;
            }
            set
            {
                if (value != this.m_value)
                {
                    int old = this.m_value;
                    this.m_value = value;
                    OnValueChanged(EventArgs.Empty);
                    OptimizedInvalidate(old, this.m_value);
                }
            }
        }

        //public Brush BackgroundDim
        //{
        //    get { return m_backgroundDim; }
        //    set { m_backgroundDim = value; }
        //}
        #endregion 属性

        #region 事件
        /// <summary>
        /// 当Value改变时引发的事件
        /// </summary>
        [Description("当Value改变时引发的事件")]
        public event EventHandler ValueChanged
        {
            add
            {
                m_onValueChanged += value;
            }
            remove
            {
                m_onValueChanged -= value;
            }
        }
        #endregion 事件
    }
}
